package edu.uci.ics.ai.maze.views;

import edu.uci.ics.ai.maze.models.GameModel;
import edu.uci.ics.ai.maze.models.Vertex;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class VisibleVertexDialog extends JDialog implements Observer {

  private final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);

  private final VisibleVertexDialogModel model;
  private final JMenuItem visibleVertMenuItem;
  private final JMenuItem searchMenuItem;
  private final JButton startButton;
  private final JButton previousButton;
  private final JButton endButton;
  private final JButton nextButton;
  private final JTextField curVertexTextField = new JTextField();
  private final JButton showAllButton;
  private final DefaultListModel vertexListModel = new DefaultListModel();
  private final JList visibleVertexList;
  
  private boolean frameSizeAdjusted = false;
  private Cursor oldCursor;

  public VisibleVertexDialog(JFrame parent, VisibleVertexDialogModel model,
          JMenuItem visibleVertMenuItem, JMenuItem searchMenuItem) {
    super(parent);

    this.model = model;
    this.visibleVertMenuItem = visibleVertMenuItem;
    this.searchMenuItem = searchMenuItem;

    setTitle("Visible Vertices");
    setResizable(false);
    getContentPane().setLayout(null);
    setSize(241, 289);
    setVisible(false);

    JLabel visVerticesLabel = new JLabel("Visible Vertices:");
    getContentPane().add(visVerticesLabel);
    visVerticesLabel.setBounds(12, 60, 96, 24);

    startButton = new JButton("|<<");
    startButton.setActionCommand("|<<");
    getContentPane().add(startButton);
    startButton.setBounds(12, 216, 59, 24);

    previousButton = new JButton("<<");
    previousButton.setActionCommand("<<");
    getContentPane().add(previousButton);
    previousButton.setBounds(72, 216, 48, 24);

    endButton = new JButton(">>|");
    endButton.setActionCommand(">>|");
    getContentPane().add(endButton);
    endButton.setBounds(168, 216, 60, 24);

    nextButton = new JButton(">>");
    nextButton.setActionCommand(">>");
    getContentPane().add(nextButton);
    nextButton.setBounds(120, 216, 48, 24);

    JLabel curVertexLabel = new JLabel("Current Vertex:");
    getContentPane().add(curVertexLabel);
    curVertexLabel.setBounds(12, 12, 96, 12);

    JButton closeButton = new JButton("Close");
    closeButton.setActionCommand("Close");
    getContentPane().add(closeButton);
    closeButton.setBounds(120, 252, 108, 24);

    curVertexTextField.setEditable(false);
    getContentPane().add(curVertexTextField);
    curVertexTextField.setBounds(12, 36, 216, 24);

    showAllButton = new JButton("Show All");
    showAllButton.setActionCommand("Show All");
    showAllButton.setSelected(true);
    getContentPane().add(showAllButton);
    showAllButton.setBounds(12, 252, 108, 25);

    JScrollPane scrollPane = new JScrollPane();
    scrollPane.setAutoscrolls(true);
    scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    scrollPane.setOpaque(true);
    getContentPane().add(scrollPane);
    scrollPane.setBounds(12, 84, 216, 120);

    visibleVertexList = new JList();
    visibleVertexList.setModel(vertexListModel);
    visibleVertexList.setAutoscrolls(false);
    visibleVertexList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    visibleVertexList.setValueIsAdjusting(true);
    visibleVertexList.setVisibleRowCount(10);
    visibleVertexList.setDoubleBuffered(true);
    scrollPane.getViewport().add(visibleVertexList);
    visibleVertexList.setBounds(0, 0, 198, 117);
    visibleVertexList.setSelectedIndex(-1);

    closeButton.addActionListener(new CloseButtonActionListener());
    startButton.addActionListener(new StartButtonActionListener());
    endButton.addActionListener(new EndButtonActionListener());
    nextButton.addActionListener(new NextButtonActionListener());
    previousButton.addActionListener(new PrevButtonActionListener());
    visibleVertexList.addListSelectionListener(new VisibleVertexListSelectionListener());
    showAllButton.addActionListener(new ShowAllButtonActionListener());

    model.addObserver(this);
  }

  @Override
  public void update(Observable obj, Object arg) {
    if (arg == GameModel.Observations.UPDATE_VIS_VERTEX_DIALOG) {
      vertexListModel.clear();
      for (String info : model.getVisibleInfo()) {
        vertexListModel.addElement(info);
      }
      curVertexTextField.setText(model.getCurVertex().toString());
      visibleVertexList.ensureIndexIsVisible(0);
      visibleVertexList.setSelectedIndex(-1);

      startButton.setEnabled(model.getNavCtrlState()[0]);
      previousButton.setEnabled(model.getNavCtrlState()[1]);
      nextButton.setEnabled(model.getNavCtrlState()[2]);
      endButton.setEnabled(model.getNavCtrlState()[3]);
      repaint();
    }
  }

  @Override
  public void setVisible(boolean visible) {
    if (visible) {
      visibleVertMenuItem.setEnabled(false);
      searchMenuItem.setEnabled(false);
      model.calcVisibleInfo(model.getStart());
      oldCursor = getCursor();
    } else {
      if (oldCursor != null) {
        setCursor(oldCursor);
      }
      visibleVertMenuItem.setEnabled(true);
      searchMenuItem.setEnabled(true);
      model.resetState();
    }
    super.setVisible(visible);
  }

  @Override
  public void addNotify() {
    // Record the size of the window prior to calling parents addNotify.
    Dimension size = getSize();

    super.addNotify();

    if (frameSizeAdjusted) {
      return;
    }
    frameSizeAdjusted = true;

    // Adjust size of frame according to the insets
    Insets insets = getInsets();
    setSize(insets.left + insets.right + size.width, insets.top + insets.bottom + size.height);
  }

  private class CloseButtonActionListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent event) {
      setVisible(false);
    }
  }

  private abstract class VertexSelectionListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent event) {
      setCursor(WAIT_CURSOR);
      model.calcVisibleInfo(next());
      setCursor(oldCursor);
    }

    public abstract Vertex next();
  }

  private class StartButtonActionListener extends VertexSelectionListener {

    @Override
    public Vertex next() {
      visibleVertexList.setSelectedIndex(1);
      return model.getStart();
    }
  }

  private class PrevButtonActionListener extends VertexSelectionListener {

    @Override
    public Vertex next() {
      int index = Math.max(0, visibleVertexList.getSelectedIndex() - 1);
      visibleVertexList.setSelectedIndex(index);
      return model.getPrev();
    }
  }

  private class NextButtonActionListener extends VertexSelectionListener {

    @Override
    public Vertex next() {
      int index = Math.min(model.getNumVisibleVertices(), visibleVertexList.getSelectedIndex() + 1);
      visibleVertexList.setSelectedIndex(index);

      return model.getNext();
    }
  }

  private class EndButtonActionListener extends VertexSelectionListener {

    @Override
    public Vertex next() {
      visibleVertexList.setSelectedIndex(model.getNumVisibleVertices());
      return model.getEnd();
    }
  }

  private class VisibleVertexListSelectionListener implements ListSelectionListener {

    @Override
    public void valueChanged(ListSelectionEvent event) {
      model.setSelIndex(visibleVertexList.getSelectedIndex());
      if (visibleVertexList.getSelectedIndex() == -1) {
        showAllButton.setEnabled(false);
      } else {
        showAllButton.setEnabled(true);
      }
    }
  }

  private class ShowAllButtonActionListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent event) {
      model.setSelIndex(-1);
      showAllButton.setEnabled(false);
    }
  }
}
